﻿using System;
using System.Collections.Generic;
using ComponentSystem;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;

namespace DataCenterSystem.EasySave3
{
    /// <summary>
    /// 内存缓存组件
    /// </summary>
    public partial class MemoryCache_Com : BaseSave_Com
    {
        /// <summary>
        /// 缓存元数据
        /// </summary>
        protected class CacheMeta
        {
            public long timestamp; // 创建时间戳
            public long lastAccess; // 最后访问时间
            public float sizeKB; // 数据大小（KB）
            public bool autoClear; //自动清理
        }
        
        /// <summary>
        /// 缓存最大容量 10MB
        /// </summary>
        private float cacheMaxSizeMB = 1f/10240;
        /// <summary>
        /// 清理器的循环间隔时间 300f
        /// </summary>
        private float cacheCleanInterval = 300f;
        /// <summary>
        /// 缓存的过期时间，过期后被清理 1800f; 
        /// </summary>
        private float cacheTTL = 1800f;          // 30分钟
        /// <summary>
        /// 缓存满时触发的事件
        /// </summary>
        public UnityAction OnCacheFull;
        /// <summary>
        /// 自动清理定时器
        /// </summary>
        private Timer autoCacheCleanerTimer;
    }

    public partial class MemoryCache_Com
    {
        /// <summary>
        /// 初始化
        /// </summary>
        protected override void Awake()
        {
            es3Settings.location = ES3.Location.Cache;
            es3Settings.compressionType = ES3.CompressionType.Gzip;
            base.Awake();
            Start_AutoCacheCleaner().Forget();
        }

        /// <summary>
        /// 添加或更新缓存
        /// </summary>
        public async UniTask AddOrUpdateCache<T>(string key, T data, bool autoClear = false)
        {
            await AddOrUpdateData(key, data);
            CacheMeta cm = await AddOrUpdateCacheMeta(key, data, autoClear);
            CheckCacheTotalSizeForAddOrUpdate(key, cm).Forget();
        }

        /// <summary>
        /// 读取数据钩子
        /// </summary>
        protected override async UniTask LoadData_Hook(string key)
        {
            await base.LoadData_Hook(key);
            await UpdateCacheMetaTime(key); 
        }

        /// <summary>
        /// 删除数据钩子
        /// </summary>
        protected override async UniTask<bool> DeletData_Hook(string key)
        {
            bool result = await base.DeletData_Hook(key);

            result = result && await DelectCacheMeta(key);
            
            return result;
        }

        #region CaheMeta

        /// <summary>
        /// 添加或更新缓存元数据
        /// </summary>
        protected async UniTask<CacheMeta> AddOrUpdateCacheMeta<T>(string key, T data, bool autoClear = false)
        {
            float dataSizeKB = await CalculateDataSize(data);
            long nowTicks = DateTime.Now.Ticks;

            string metaKey = $"{key}_meta";
            CacheMeta cm = null;

            if (await ContainsKey(metaKey))
            {
                cm = ES3.Load<CacheMeta>(metaKey, es3Settings);
                cm.lastAccess = nowTicks;
                cm.sizeKB = dataSizeKB;
                cm.autoClear = autoClear;
            }
            else
            {
                cm = new CacheMeta
                {
                    timestamp = nowTicks,
                    lastAccess = nowTicks,
                    sizeKB = dataSizeKB,
                    autoClear = autoClear
                };
            }
            
            ES3.Save(metaKey, cm, es3Settings);

            return cm;
        }

        /// <summary>
        /// 更新缓存元数据时间
        /// </summary>
        protected async UniTask UpdateCacheMetaTime(string key)
        {
            string metaKey = $"{key}_meta";
            if(!await ContainsKey(metaKey)) return;
            CacheMeta meta = await GetCacheMeta(key);
            meta.lastAccess = DateTime.Now.Ticks;
            ES3.Save(metaKey, meta, es3Settings);
        }

        /// <summary>
        /// 删除缓存元数据
        /// </summary>
        protected async UniTask<bool> DelectCacheMeta(string key)
        {
            string metaKey = $"{key}_meta";
            
            //不存在 视为已经删了
            if(!await ContainsKey(metaKey)) return true;

            try
            {
                ES3.DeleteKey(metaKey, es3Settings);
                return true;
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError($"DelectCacheMeta() 删除数据异常 key: {key} -- e: {e.Message}");
                return false;
            }
        }

        /// <summary>
        /// 获取缓存元数据
        /// </summary>
        protected async UniTask<CacheMeta> GetCacheMeta(string key)
        {
            string metaKey = $"{key}_meta";
            
            if (!await ContainsKey(metaKey))
            {
                Debug.LogError($"MemoryCache_Com GetCacheMeta() 获取元数据失败 key: {metaKey}");
                return default(CacheMeta);
            }
            
            return ES3.Load<CacheMeta>($"{metaKey}", es3Settings);
        }

        #endregion

        #region 容量

        /// <summary>
        /// 检查缓存容量
        /// </summary>
        protected async UniTask CheckCacheTotalSizeForAddOrUpdate(string key,CacheMeta cm)
        {
            float currentSize = await GetCacheTotalSize();
            
            if (currentSize > cacheMaxSizeMB )
            {
                Debug.LogError($"MemoryCache_Com AddOrUpdateCache 缓存容量已达警示线 {currentSize / 1024:F1}MB / {cacheMaxSizeMB}MB -- 更新的内容 key: {key} -- 更新的总容量(不包含更新差值): {cm.sizeKB}");
                OnCacheFull?.Invoke();
            }
        }

        /// <summary>
        /// 计算数据大小
        /// </summary>
        protected async UniTask<float> CalculateDataSize<T>(T data)
        {
            byte[] bytes = ES3.Serialize(data, es3Settings);
            return bytes.Length / 1024f; // KB单位
        }

        /// <summary>
        /// 获取缓存总大小
        /// </summary>
        protected async UniTask<float> GetCacheTotalSize()
        {
            float total = 0;
            foreach (var key in cachedKeys)
                if (ES3.KeyExists($"{key}_meta", es3Settings))
                    total += ES3.Load<CacheMeta>($"{key}_meta", es3Settings).sizeKB; //KB单位
            return total;
        }

        #endregion
        
        #region 自动清理

        /// <summary>
        /// 启动自动清理
        /// </summary>
        public async UniTaskVoid Start_AutoCacheCleaner()
        {
            if(autoCacheCleanerTimer != null && autoCacheCleanerTimer.IsRunning) return;
            
            autoCacheCleanerTimer = new Timer(float.PositiveInfinity, cacheCleanInterval);
            autoCacheCleanerTimer.OnTimerTick += CleanExpiredCache;
            
            autoCacheCleanerTimer.Start();
        }

        /// <summary>
        /// 停止自动清理
        /// </summary>
        public async UniTaskVoid Stop_AutoCacheCleaner()
        {
            if(autoCacheCleanerTimer == null || !autoCacheCleanerTimer.IsRunning) return;
            
            autoCacheCleanerTimer.Stop();
            autoCacheCleanerTimer = null;
        }

        /// <summary>
        /// 清理过期缓存
        /// </summary>
        private async UniTask CleanExpiredCache()
        {
            Debug.Log($"MemoryCache_Com CleanExpiredCache 执行内存自动清理 ");
            List<string> expiredKeys = new List<string>();

            foreach (var key in cachedKeys.Keys)
            {
                if (!ES3.KeyExists($"{key}_meta", es3Settings)) continue;
                
                var meta = ES3.Load<CacheMeta>($"{key}_meta", es3Settings);
                if(!meta.autoClear) continue;
                var lifespan = (DateTime.Now - new DateTime(meta.lastAccess)).TotalSeconds;

                if (lifespan > cacheTTL) expiredKeys.Add(key);
            }

            foreach (var key in expiredKeys)
            {
                DeletData(key);
            }
            
            Debug.Log($"MemoryCache_Com CleanExpiredCache 已执行清理过期缓存 共清理 {expiredKeys.Count} 个");
        }

        #endregion
        
    }
}